{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "张量结构与操作.ipynb",
      "provenance": [],
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bRoQpPQORTki"
      },
      "source": [
        "# 张量数据结构\n",
        "\n",
        "张量和计算图是 TensorFlow 的核心概念。Tensorflow 的基本数据结构是张量 Tensor。张量即多维数组。Tensorflow 的张量和 numpy 中的 array 很类似。从行为特性来看，有两种类型的张量，常量constant 和变量 Variable。常量的值在计算图中不可以被重新赋值，变量可以在计算图中用 assign 等算子重新赋值。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "RgvR-qxTlxSu"
      },
      "source": [
        "import numpy as np\n",
        "import tensorflow as tf"
      ],
      "execution_count": 1,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "u4HG22_KRl-f"
      },
      "source": [
        "## 常量张量\n",
        "\n",
        "张量的数据类型和 numpy.array 基本一一对应。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ATPOe2VHRo6r",
        "outputId": "504c68bb-57b8-4d96-d89e-567c618bc6a6",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 85
        }
      },
      "source": [
        "\n",
        "i = tf.constant(1) # tf.int32 类型常量\n",
        "l = tf.constant(1,dtype = tf.int64) # tf.int64 类型常量\n",
        "f = tf.constant(1.23) #tf.float32 类型常量\n",
        "d = tf.constant(3.14,dtype = tf.double) # tf.double 类型常量\n",
        "s = tf.constant(\"hello world\") # tf.string类型常量\n",
        "b = tf.constant(True) #tf.bool类型常量\n",
        "\n",
        "\n",
        "print(tf.int64 == np.int64) \n",
        "print(tf.bool == np.bool)\n",
        "print(tf.double == np.float64)\n",
        "print(tf.string == np.unicode) # tf.string类型和np.unicode类型不等价"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "True\n",
            "True\n",
            "True\n",
            "False\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "xmUrgttzTJgK"
      },
      "source": [
        "不同类型的数据可以用不同维度(rank)的张量来表示。标量为0维张量，向量为1维张量，矩阵为2维张量。\n",
        "彩色图像有rgb三个通道，可以表示为3维张量。视频还有时间维，可以表示为4维张量。\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "GaN8iz4fTXil"
      },
      "source": [
        "scalar = tf.constant(True)  # 标量，0 维张量\n",
        "\n",
        "print(tf.rank(scalar))\n",
        "print(scalar.numpy().ndim)  # tf.rank的作用和numpy的ndim方法相同\n",
        "\n",
        "vector = tf.constant([1.0,2.0,3.0,4.0]) # 向量，1维张量\n",
        "\n",
        "print(tf.rank(vector))\n",
        "print(np.ndim(vector.numpy()))\n",
        "\n",
        "matrix = tf.constant([[1.0,2.0],[3.0,4.0]]) # 矩阵, 2维张量\n",
        "\n",
        "print(tf.rank(matrix).numpy())\n",
        "print(np.ndim(matrix))\n",
        "\n",
        "tensor3 = tf.constant([[[1.0,2.0],[3.0,4.0]],[[5.0,6.0],[7.0,8.0]]])  # 3维张量\n",
        "print(tensor3)\n",
        "print(tf.rank(tensor3))\n",
        "\n",
        "tensor4 = tf.constant([[[[1.0,1.0],[2.0,2.0]],[[3.0,3.0],[4.0,4.0]]],\n",
        "                        [[[5.0,5.0],[6.0,6.0]],[[7.0,7.0],[8.0,8.0]]]])  # 4维张量\n",
        "print(tensor4)\n",
        "print(tf.rank(tensor4))\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3_1JDqoPTku1"
      },
      "source": [
        "可以用tf.cast改变张量的数据类型。可以用numpy方法将tensorflow中的张量转化成numpy中的张量。可以用shape方法查看张量的尺寸。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "2cI0mf7bTn33"
      },
      "source": [
        "h = tf.constant([123,456],dtype = tf.int32)\n",
        "f = tf.cast(h,tf.float32)\n",
        "print(h.dtype, f.dtype)\n",
        "\n",
        "y = tf.constant([[1.0,2.0],[3.0,4.0]])\n",
        "print(y.numpy()) #转换成np.array\n",
        "print(y.shape)\n",
        "\n",
        "u = tf.constant(u\"你好 世界\")\n",
        "print(u.numpy())  \n",
        "print(u.numpy().decode(\"utf-8\"))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YMOuIk-hTt_n"
      },
      "source": [
        "## 变量张量\n",
        "\n",
        "模型中需要被训练的参数一般被设置成变量。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "WEbOiGXATxsv"
      },
      "source": [
        "# 常量值不可以改变，常量的重新赋值相当于创造新的内存空间\n",
        "c = tf.constant([1.0,2.0])\n",
        "print(c)\n",
        "print(id(c))\n",
        "c = c + tf.constant([1.0,1.0])\n",
        "print(c)\n",
        "print(id(c))\n",
        "\n",
        "# 变量的值可以改变，可以通过assign, assign_add等方法给变量重新赋值\n",
        "v = tf.Variable([1.0,2.0],name = \"v\")\n",
        "print(v)\n",
        "print(id(v))\n",
        "v.assign_add([1.0,1.0])\n",
        "print(v)\n",
        "print(id(v))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SAbCtLVok6Z4"
      },
      "source": [
        "# 张量的结构操作\n",
        "\n",
        "张量的操作主要包括张量的结构操作和张量的数学运算。张量结构操作诸如：张量创建，索引切片，维度变换，合并分割。张量数学运算主要有：标量运算，向量运算，矩阵运算。另外我们会介绍张量运算的广播机制。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jcxWcWjGlReJ"
      },
      "source": [
        "## 张量创建"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jRYIo-ZElTVr",
        "outputId": "376a681c-a7e2-48bd-c8ad-4f6a0cbe53d5",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 289
        }
      },
      "source": [
        "a = tf.constant([1,2,3],dtype = tf.float32)\n",
        "tf.print(a)\n",
        "\n",
        "b = tf.range(1,10,delta = 2)\n",
        "tf.print(b)\n",
        "\n",
        "c = tf.linspace(0.0,2*3.14,100)\n",
        "tf.print(c)\n",
        "\n",
        "d = tf.zeros([3,3])\n",
        "tf.print(d)\n",
        "\n",
        "a = tf.ones([3,3])\n",
        "b = tf.zeros_like(a,dtype= tf.float32)\n",
        "tf.print(a)\n",
        "tf.print(b)\n",
        "\n",
        "b = tf.fill([3,2],5)\n",
        "tf.print(b)\n",
        "\n",
        "# 均匀分布随机\n",
        "tf.random.set_seed(1.0)\n",
        "a = tf.random.uniform([5],minval=0,maxval=10)\n",
        "tf.print(a)\n",
        "\n",
        "# 正态分布随机\n",
        "b = tf.random.normal([3,3],mean=0.0,stddev=1.0)\n",
        "tf.print(b)\n",
        "\n",
        "# 正态分布随机，剔除2倍方差以外数据重新生成\n",
        "c = tf.random.truncated_normal((5,5), mean=0.0, stddev=1.0, dtype=tf.float32)\n",
        "tf.print(c)\n",
        "\n",
        "# 特殊矩阵\n",
        "I = tf.eye(3,3) #单位矩阵\n",
        "tf.print(I)\n",
        "tf.print(\" \")\n",
        "t = tf.linalg.diag([1,2,3]) #对角阵\n",
        "tf.print(t)"
      ],
      "execution_count": 3,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[1 2 3]\n",
            "[1 3 5 7 9]\n",
            "[0 0.0634343475 0.126868695 ... 6.15313148 6.21656609 6.28]\n",
            "[[0 0 0]\n",
            " [0 0 0]\n",
            " [0 0 0]]\n",
            "[[1 1 1]\n",
            " [1 1 1]\n",
            " [1 1 1]]\n",
            "[[0 0 0]\n",
            " [0 0 0]\n",
            " [0 0 0]]\n",
            "[[5 5]\n",
            " [5 5]\n",
            " [5 5]]\n",
            "[1.65130854 9.01481247 6.30974197 4.34546089 2.9193902]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XXr4l_YWmbt7"
      },
      "source": [
        "## 索引切片\n",
        "\n",
        "张量的索引切片方式和 numpy 几乎是一样的。切片时支持缺省参数和省略号。对于 tf.Variable,可以通过索引和切片对部分元素进行修改。对于提取张量的连续子区域，也可以使用 tf.slice，此外，对于不规则的切片提取,可以使用tf.gather,tf.gather_nd,tf.boolean_mask。\n",
        "\n",
        "tf.boolean_mask功能最为强大，它可以实现tf.gather,tf.gather_nd的功能，并且 tf.boolean_mask还可以实现布尔索引。如果要通过修改张量的某些元素得到新的张量，可以使用 tf.where，tf.scatter_nd。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "rcZukBg8mo-p",
        "outputId": "ade68d76-90a9-4c92-9e33-9db6f565a0b5",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 102
        }
      },
      "source": [
        "tf.random.set_seed(3)\n",
        "t = tf.random.uniform([5,5],minval=0,maxval=10,dtype=tf.int32)\n",
        "tf.print(t)"
      ],
      "execution_count": 4,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[4 7 4 2 9]\n",
            " [9 1 2 4 7]\n",
            " [7 2 7 4 0]\n",
            " [9 6 9 7 2]\n",
            " [3 7 0 0 3]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "DtMRvNG-m0E0"
      },
      "source": [
        "# 第0行\n",
        "tf.print(t[0])\n",
        "\n",
        "# 倒数第一行\n",
        "tf.print(t[-1])\n",
        "\n",
        "# 第1行第3列\n",
        "tf.print(t[1,3])\n",
        "tf.print(t[1][3])\n",
        "\n",
        "# 第1行至第3行\n",
        "tf.print(t[1:4,:])\n",
        "tf.print(tf.slice(t,[1,0],[3,5])) #tf.slice(input,begin_vector,size_vector)\n",
        "\n",
        "# 第1行至最后一行，第0列到最后一列每隔两列取一列\n",
        "tf.print(t[1:4,:4:2])"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ikJeK1Qym-Yv"
      },
      "source": [
        "# 对变量来说，还可以使用索引和切片修改部分元素\n",
        "x = tf.Variable([[1,2],[3,4]],dtype = tf.float32)\n",
        "x[1,:].assign(tf.constant([0.0,0.0]))\n",
        "tf.print(x)\n",
        "\n",
        "a = tf.random.uniform([3,3,3],minval=0,maxval=10,dtype=tf.int32)\n",
        "tf.print(a)\n",
        "\n",
        "# 省略号可以表示多个冒号\n",
        "tf.print(a[...,1])"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FlzmDf0XnBoG"
      },
      "source": [
        "以上切片方式相对规则，对于不规则的切片提取,可以使用 tf.gather,tf.gather_nd,tf.boolean_mask。考虑班级成绩册的例子，有4个班级，每个班级10个学生，每个学生7门科目成绩。可以用一个4×10×7的张量来表示。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Poh-ys_MnH_M",
        "outputId": "b9ef7d0e-e619-42ed-b6d2-536c4312daaa",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 544
        }
      },
      "source": [
        "scores = tf.random.uniform((4,10,7),minval=0,maxval=100,dtype=tf.int32)\n",
        "tf.print(scores)"
      ],
      "execution_count": 5,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[[27 83 99 ... 70 67 79]\n",
            "  [6 97 31 ... 3 40 48]\n",
            "  [71 63 31 ... 44 30 86]\n",
            "  ...\n",
            "  [10 94 95 ... 89 1 13]\n",
            "  [61 6 81 ... 24 46 10]\n",
            "  [61 20 89 ... 33 95 78]]\n",
            "\n",
            " [[26 1 5 ... 2 62 29]\n",
            "  [40 0 68 ... 37 15 28]\n",
            "  [70 60 82 ... 72 64 1]\n",
            "  ...\n",
            "  [49 60 74 ... 18 87 35]\n",
            "  [2 38 41 ... 66 8 74]\n",
            "  [37 12 60 ... 57 29 51]]\n",
            "\n",
            " [[33 79 23 ... 5 24 45]\n",
            "  [66 50 83 ... 32 30 58]\n",
            "  [47 91 21 ... 59 81 1]\n",
            "  ...\n",
            "  [98 93 66 ... 55 67 69]\n",
            "  [71 93 26 ... 77 11 63]\n",
            "  [90 75 74 ... 7 68 14]]\n",
            "\n",
            " [[72 1 53 ... 97 57 28]\n",
            "  [24 5 45 ... 41 86 23]\n",
            "  [99 68 54 ... 97 65 85]\n",
            "  ...\n",
            "  [94 4 69 ... 88 17 93]\n",
            "  [28 48 9 ... 73 32 39]\n",
            "  [18 2 40 ... 51 37 24]]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Vc9mkzEtnMk3",
        "outputId": "87567254-c137-4c43-e081-22073f451edc",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 527
        }
      },
      "source": [
        "# 抽取每个班级第0个学生，第5个学生，第9个学生的全部成绩\n",
        "p = tf.gather(scores,[0,5,9],axis=1)\n",
        "tf.print(p)\n",
        "\n",
        "# 抽取每个班级第0个学生，第5个学生，第9个学生的第1门课程，第3门课程，第6门课程成绩\n",
        "q = tf.gather(tf.gather(scores,[0,5,9],axis=1),[1,3,6],axis=2)\n",
        "tf.print(q)\n",
        "\n",
        "\n",
        "# 抽取第0个班级第0个学生，第2个班级的第4个学生，第3个班级的第6个学生的全部成绩\n",
        "#indices的长度为采样样本的个数，每个元素为采样位置的坐标\n",
        "s = tf.gather_nd(scores,indices = [(0,0),(2,4),(3,6)])\n",
        "s"
      ],
      "execution_count": 6,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[[27 83 99 ... 70 67 79]\n",
            "  [96 78 75 ... 2 3 49]\n",
            "  [61 20 89 ... 33 95 78]]\n",
            "\n",
            " [[26 1 5 ... 2 62 29]\n",
            "  [16 51 72 ... 11 80 78]\n",
            "  [37 12 60 ... 57 29 51]]\n",
            "\n",
            " [[33 79 23 ... 5 24 45]\n",
            "  [46 73 64 ... 27 39 13]\n",
            "  [90 75 74 ... 7 68 14]]\n",
            "\n",
            " [[72 1 53 ... 97 57 28]\n",
            "  [84 99 85 ... 79 57 62]\n",
            "  [18 2 40 ... 51 37 24]]]\n",
            "[[[83 9 79]\n",
            "  [78 17 49]\n",
            "  [20 9 78]]\n",
            "\n",
            " [[1 17 29]\n",
            "  [51 94 78]\n",
            "  [12 45 51]]\n",
            "\n",
            " [[79 82 45]\n",
            "  [73 71 13]\n",
            "  [75 12 14]]\n",
            "\n",
            " [[1 87 28]\n",
            "  [99 71 62]\n",
            "  [2 74 24]]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "z2CnEn-onXHa"
      },
      "source": [
        "以上tf.gather和tf.gather_nd的功能也可以用tf.boolean_mask来实现。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "dTDJ1Kw0nXxZ",
        "outputId": "eaeb0acc-5b83-4739-be0e-505e4d2a5249",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 442
        }
      },
      "source": [
        "# 抽取每个班级第0个学生，第5个学生，第9个学生的全部成绩\n",
        "p = tf.boolean_mask(scores,[True,False,False,False,False,\n",
        "                            True,False,False,False,True],axis=1)\n",
        "tf.print(p)\n",
        "\n",
        "# 抽取第0个班级第0个学生，第2个班级的第4个学生，第3个班级的第6个学生的全部成绩\n",
        "s = tf.boolean_mask(scores,\n",
        "    [[True,False,False,False,False,False,False,False,False,False],\n",
        "     [False,False,False,False,False,False,False,False,False,False],\n",
        "     [False,False,False,False,True,False,False,False,False,False],\n",
        "     [False,False,False,False,False,False,True,False,False,False]])\n",
        "tf.print(s)\n",
        "\n",
        "# 利用tf.boolean_mask可以实现布尔索引\n",
        "\n",
        "# 找到矩阵中小于0的元素\n",
        "c = tf.constant([[-1,1,-1],[2,2,-2],[3,-3,3]],dtype=tf.float32)\n",
        "tf.print(c,\"\\n\")\n",
        "\n",
        "tf.print(tf.boolean_mask(c,c<0),\"\\n\") \n",
        "tf.print(c[c<0]) #布尔索引，为boolean_mask的语法糖形式"
      ],
      "execution_count": 7,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[[27 83 99 ... 70 67 79]\n",
            "  [96 78 75 ... 2 3 49]\n",
            "  [61 20 89 ... 33 95 78]]\n",
            "\n",
            " [[26 1 5 ... 2 62 29]\n",
            "  [16 51 72 ... 11 80 78]\n",
            "  [37 12 60 ... 57 29 51]]\n",
            "\n",
            " [[33 79 23 ... 5 24 45]\n",
            "  [46 73 64 ... 27 39 13]\n",
            "  [90 75 74 ... 7 68 14]]\n",
            "\n",
            " [[72 1 53 ... 97 57 28]\n",
            "  [84 99 85 ... 79 57 62]\n",
            "  [18 2 40 ... 51 37 24]]]\n",
            "[[27 83 99 ... 70 67 79]\n",
            " [44 94 85 ... 65 80 71]\n",
            " [88 35 53 ... 51 2 89]]\n",
            "[[-1 1 -1]\n",
            " [2 2 -2]\n",
            " [3 -3 3]] \n",
            "\n",
            "[-1 -1 -2 -3] \n",
            "\n",
            "[-1 -1 -2 -3]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lz5jfzIlninB"
      },
      "source": [
        "以上这些方法仅能提取张量的部分元素值，但不能更改张量的部分元素值得到新的张量。如果要通过修改张量的部分元素值得到新的张量，可以使用tf.where和tf.scatter_nd。\n",
        "\n",
        "- tf.where可以理解为if的张量版本，此外它还可以用于找到满足条件的所有元素的位置坐标。\n",
        "\n",
        "- tf.scatter_nd的作用和tf.gather_nd有些相反，tf.gather_nd用于收集张量的给定位置的元素，而tf.scatter_nd可以将某些值插入到一个给定shape的全0的张量的指定位置处。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "14XicHkZnlVw",
        "outputId": "ce0965be-c6e7-4cbe-895f-53bd6efa85fb",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 85
        }
      },
      "source": [
        "# 找到张量中小于0的元素,将其换成np.nan得到新的张量\n",
        "# tf.where和np.where作用类似，可以理解为if的张量版本\n",
        "\n",
        "c = tf.constant([[-1,1,-1],[2,2,-2],[3,-3,3]],dtype=tf.float32)\n",
        "d = tf.where(c<0,tf.fill(c.shape,np.nan),c) \n",
        "d\n",
        "\n",
        "# 如果where只有一个参数，将返回所有满足条件的位置坐标\n",
        "indices = tf.where(c<0)\n",
        "indices\n",
        "\n",
        "# 将张量的第[0,0]和[2,1]两个位置元素替换为0得到新的张量\n",
        "d = c - tf.scatter_nd([[0,0],[2,1]],[c[0,0],c[2,1]],c.shape)\n",
        "d\n",
        "\n",
        "# scatter_nd的作用和gather_nd有些相反\n",
        "#可以将某些值插入到一个给定shape的全0的张量的指定位置处。\n",
        "indices = tf.where(c<0)\n",
        "tf.scatter_nd(indices,tf.gather_nd(c,indices),c.shape)"
      ],
      "execution_count": 8,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<tf.Tensor: shape=(3, 3), dtype=float32, numpy=\n",
              "array([[-1.,  0., -1.],\n",
              "       [ 0.,  0., -2.],\n",
              "       [ 0., -3.,  0.]], dtype=float32)>"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 8
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dzfcexD_oE3C"
      },
      "source": [
        "## 维度变换\n",
        "\n",
        "维度变换相关函数主要有 tf.reshape, tf.squeeze, tf.expand_dims, tf.transpose.\n",
        "\n",
        "- tf.reshape 可以改变张量的形状。\n",
        "- tf.squeeze 可以减少维度。\n",
        "- tf.expand_dims 可以增加维度。\n",
        "- tf.transpose 可以交换维度。\n",
        "\n",
        "tf.reshape可以改变张量的形状，但是其本质上不会改变张量元素的存储顺序，所以，该操作实际上非常迅速，并且是可逆的。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "O-GdvSteoOKj",
        "outputId": "15e97e48-a7c7-4d09-fe7c-c199801f3cd2",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 221
        }
      },
      "source": [
        "a = tf.random.uniform(shape=[1,3,3,2],\n",
        "                      minval=0,maxval=255,dtype=tf.int32)\n",
        "tf.print(a.shape)\n",
        "tf.print(a)"
      ],
      "execution_count": 9,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "TensorShape([1, 3, 3, 2])\n",
            "[[[[242 247]\n",
            "   [26 215]\n",
            "   [237 156]]\n",
            "\n",
            "  [[229 38]\n",
            "   [26 154]\n",
            "   [26 128]]\n",
            "\n",
            "  [[148 66]\n",
            "   [147 128]\n",
            "   [146 173]]]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "RjlMYRFZoQSP",
        "outputId": "6d682a28-9be0-4b62-bf1d-4c75f5f8d82f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 85
        }
      },
      "source": [
        "# 改成 （3,6）形状的张量\n",
        "b = tf.reshape(a,[3,6])\n",
        "tf.print(b.shape)\n",
        "tf.print(b)"
      ],
      "execution_count": 10,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "TensorShape([3, 6])\n",
            "[[242 247 26 215 237 156]\n",
            " [229 38 26 154 26 128]\n",
            " [148 66 147 128 146 173]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "V_UTJ0WkoTGK"
      },
      "source": [
        "# 改回成 [1,3,3,2] 形状的张量\n",
        "c = tf.reshape(b,[1,3,3,2])\n",
        "tf.print(c)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Qx5r3OrKoVQd"
      },
      "source": [
        "如果张量在某个维度上只有一个元素，利用tf.squeeze可以消除这个维度。和tf.reshape相似，它本质上不会改变张量元素的存储顺序。张量的各个元素在内存中是线性存储的，其一般规律是，同一层级中的相邻元素的物理地址也相邻。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "AjHtlUdQoXZp",
        "outputId": "03224a04-2048-4962-ac60-b637fd411e53",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 221
        }
      },
      "source": [
        "s = tf.squeeze(a)\n",
        "tf.print(s.shape)\n",
        "tf.print(s)\n",
        "\n",
        "d = tf.expand_dims(s,axis=0) #在第0维插入长度为1的一个维度\n",
        "d"
      ],
      "execution_count": 11,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "TensorShape([3, 3, 2])\n",
            "[[[242 247]\n",
            "  [26 215]\n",
            "  [237 156]]\n",
            "\n",
            " [[229 38]\n",
            "  [26 154]\n",
            "  [26 128]]\n",
            "\n",
            " [[148 66]\n",
            "  [147 128]\n",
            "  [146 173]]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QqLWGTFXoZ24"
      },
      "source": [
        "tf.transpose可以交换张量的维度，与tf.reshape不同，它会改变张量元素的存储顺序。tf.transpose常用于图片存储格式的变换上。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "q36EYYNIob8v",
        "outputId": "b04ecb7f-ed58-439c-fe4e-b9a4797f48a1",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "source": [
        "# Batch,Height,Width,Channel\n",
        "a = tf.random.uniform(shape=[100,600,600,4],minval=0,maxval=255,dtype=tf.int32)\n",
        "tf.print(a.shape)\n",
        "\n",
        "# 转换成 Channel,Height,Width,Batch\n",
        "s= tf.transpose(a,perm=[3,1,2,0])\n",
        "tf.print(s.shape)"
      ],
      "execution_count": 12,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "TensorShape([100, 600, 600, 4])\n",
            "TensorShape([4, 600, 600, 100])\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vB5Eglm-ofCz"
      },
      "source": [
        "## 合并分割\n",
        "\n",
        "和numpy类似，可以用tf.concat和tf.stack方法对多个张量进行合并，可以用tf.split方法把一个张量分割成多个张量。tf.concat和tf.stack有略微的区别，tf.concat是连接，不会增加维度，而tf.stack是堆叠，会增加维度。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "XDUwJlqYokKS",
        "outputId": "f2926f09-78d1-4846-8bb7-1c601ef87771",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 136
        }
      },
      "source": [
        "a = tf.constant([[1.0,2.0],[3.0,4.0]])\n",
        "b = tf.constant([[5.0,6.0],[7.0,8.0]])\n",
        "c = tf.constant([[9.0,10.0],[11.0,12.0]])\n",
        "\n",
        "tf.concat([a,b,c],axis = 0)"
      ],
      "execution_count": 13,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<tf.Tensor: shape=(6, 2), dtype=float32, numpy=\n",
              "array([[ 1.,  2.],\n",
              "       [ 3.,  4.],\n",
              "       [ 5.,  6.],\n",
              "       [ 7.,  8.],\n",
              "       [ 9., 10.],\n",
              "       [11., 12.]], dtype=float32)>"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 13
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "k1Co1E8noq1O",
        "outputId": "7104654c-f110-48bb-a65b-5fc835ff7bdc",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 68
        }
      },
      "source": [
        "tf.concat([a,b,c],axis = 1)\n"
      ],
      "execution_count": 16,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<tf.Tensor: shape=(2, 6), dtype=float32, numpy=\n",
              "array([[ 1.,  2.,  5.,  6.,  9., 10.],\n",
              "       [ 3.,  4.,  7.,  8., 11., 12.]], dtype=float32)>"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 16
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ZR2KG1xfo5t1",
        "outputId": "73276cbe-b9f5-485c-8056-7d2441728b60",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 170
        }
      },
      "source": [
        "tf.stack([a,b,c])\n"
      ],
      "execution_count": 17,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<tf.Tensor: shape=(3, 2, 2), dtype=float32, numpy=\n",
              "array([[[ 1.,  2.],\n",
              "        [ 3.,  4.]],\n",
              "\n",
              "       [[ 5.,  6.],\n",
              "        [ 7.,  8.]],\n",
              "\n",
              "       [[ 9., 10.],\n",
              "        [11., 12.]]], dtype=float32)>"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 17
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "j-2_BE_-o8AT",
        "outputId": "953353eb-44f0-4977-b694-07b9d6a115c5",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 153
        }
      },
      "source": [
        "tf.stack([a,b,c],axis=1)\n"
      ],
      "execution_count": 18,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<tf.Tensor: shape=(2, 3, 2), dtype=float32, numpy=\n",
              "array([[[ 1.,  2.],\n",
              "        [ 5.,  6.],\n",
              "        [ 9., 10.]],\n",
              "\n",
              "       [[ 3.,  4.],\n",
              "        [ 7.,  8.],\n",
              "        [11., 12.]]], dtype=float32)>"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 18
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "tbmxe3dxo-H1"
      },
      "source": [
        "a = tf.constant([[1.0,2.0],[3.0,4.0]])\n",
        "b = tf.constant([[5.0,6.0],[7.0,8.0]])\n",
        "c = tf.constant([[9.0,10.0],[11.0,12.0]])\n",
        "\n",
        "c = tf.concat([a,b,c],axis = 0)"
      ],
      "execution_count": 20,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c2hbkYwGpBmC"
      },
      "source": [
        "tf.split是tf.concat的逆运算，可以指定分割份数平均分割，也可以通过指定每份的记录数量进行分割。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "XdCwgEOqpDJS",
        "outputId": "796427bb-f404-46c6-b33f-a9bacc74c02c",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 170
        }
      },
      "source": [
        "#tf.split(value,num_or_size_splits,axis)\n",
        "tf.split(c,3,axis = 0)  #指定分割份数，平均分割"
      ],
      "execution_count": 21,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[<tf.Tensor: shape=(2, 2), dtype=float32, numpy=\n",
              " array([[1., 2.],\n",
              "        [3., 4.]], dtype=float32)>,\n",
              " <tf.Tensor: shape=(2, 2), dtype=float32, numpy=\n",
              " array([[5., 6.],\n",
              "        [7., 8.]], dtype=float32)>,\n",
              " <tf.Tensor: shape=(2, 2), dtype=float32, numpy=\n",
              " array([[ 9., 10.],\n",
              "        [11., 12.]], dtype=float32)>]"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 21
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "WeGebXEwpDjw"
      },
      "source": [
        "tf.split(c,[2,2,2],axis = 0) #指定每份的记录数量\n"
      ],
      "execution_count": null,
      "outputs": []
    }
  ]
}